/*-+-*****************************************************************************
 Copyright (C) 2009-2012 Oceanside Software Corporation (R)
 Dallas, Texas

 Author       : Jason T. Brower
 Purpose      : This file is part of a point-of-sale software package
                originally written for Table-Service Restaurants.
              
 Date         : July 8th of 2012
              
 History      : The application that this software file belongs to was 
                written over a 3-Year period.  The first version was written
                in C# .Net 3.5 using Windows Forms.  Being new to .Net at
                the time, I soon learned of Windows Presentation Foundation,
                liked the graphics capabilities and databinding so I
                migrated to WPF.  When the .Net 4.0 framework was released
                the application was migrated from 3.5 to 4.0. Now today,
                .Net 4.5 RC is out and I was already in process of migrating
                before I made this public.
                
Excuse Me     : Please excuse the coding style, it is not representative of
                my typical work.  During the three years I was under a very
                tight budget, constantly running out of funding and was 
                working 18 hour days hyped up on 5-Hour Energy.  The 
                design is not textbook.  It is monolythic because of
                previous issues I will not get into.  There are few comments
                in the code and the ones that are there may be incorrect.
                
What it is not: This is not the exact version that I took through PA-DSS 2.0
                nor is it the exact version running at my customer sites.
                I removed some details for security purposes including some
                encryption algorithms.  The reporting was removed because
                I was including a third party library that cannot be 
                published with this Open Source.
                
What it is    : This application has been running at high volume restaraunts
                for over a year.  It never freezes, crashes or does anything
                unexpected.  So long as it is built, installed and configured
                correctly, it is a strong system that we can all enhance.

Contact       : Please note that I can no longer work for free.  However, you
                can reach me at information@oceansidesw.com.                                                      

*********************************************************************************/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Oceanside.Payments;
using System.Linq;
using System.Threading;
using Oceanside.Payments.Credit;


#if _LOCALDB
  using Oceanside.LocalDB;
#else
using Oceanside.RemoteDB;
#endif
namespace Oceanside
{
    public static class MagCardDelegates
    {
        public static void ProcessPrepaidBalance(MagneticCard magCard, Payments.Prepaid.Balance bdata)
        {
            try
            {
                App.InvokeStopStoryBoard_NOTHROW();
                string response = bdata.Request(magCard, SHIFT.GlobalContext, SHIFT.Current.UserId);

                TRANSACTION_RESPONSE balResponse = new TRANSACTION_RESPONSE();
                balResponse.Parse(response);

                if (balResponse.CmdStatusOk)
                {
                    UMD.ShowGuiMessageSTASafe_NOTHROW("The card you swiped has a prepaid balance of $"
                        + balResponse.Balance + ".");
                }
                else
                {
                    UMD.ShowGuiMessageSTASafe_NOTHROW("The processor returned an error code of ["
    + balResponse.CmdStatus + "] and a message of [" + balResponse.TextResponse + "].");
                }
            }
            catch (Exception ex)
            {
                Logging.LogException(ex);
                UMD.ShowGuiMessageSTASafe_NOTHROW("An error occured while processing the transaction.");
            }
        }

        public static void FinishCardRequest(CACHE c, string response, bool isPrepaidGift, MagneticCard magCard, bool partialAmountAuthed)
        {
            App.InvokeStopStoryBoard_NOTHROW();
            ///This must be here so we generate a DB assigned ID to be used below.
            ///----------------------------------------------------------------
            TRANSACTION_RESPONSE respMessage = new TRANSACTION_RESPONSE();
            SHIFT.GlobalContext.TRANSACTION_RESPONSES.AddObject(respMessage);
            respMessage.Parse(response);
            SHIFT.GlobalContext.SaveChanges();
            ///----------------------------------------------------------------

            if (respMessage.CmdStatusOk)
            {
                SHIFT.GlobalContext.CACHES.AddObject(c);
                SHIFT.GlobalContext.SaveChanges();

                TRANSACTION t = new TRANSACTION();
                if (isPrepaidGift)
                {
                    t.TransactionType = TRANSACTION.CONST_PAY_GIFT;
                    t.TenderType = TRANSACTION.CONST_TENDER_GIFT;
                }
                else
                {
                    t.TransactionType = TRANSACTION.CONST_PAY_CREDIT;
                    t.TenderType = MagneticCard.GetTenderType(magCard.Pan);
                }
                t.BusinessDayId = BUSINESS_DAY.GetNow(SHIFT.GlobalContext).Id;
                t.ExtId = c.Id;
                t.Base = respMessage.AuthorizeDec;
                t.OpenDateStamp = DateTime.Now;
                t.LastModifiedDateStamp = DateTime.Now;
                t.ORDER = SHIFT.SelectedOrder;
                t.PaidAtRevenueCenterId = KIOSK.Get(SHIFT.GlobalContext).RevenueCenterId;
                t.TransactionTypeId = respMessage.Id;
                t.CustomerName = magCard.Name;
                t.TerminalId = KIOSK.Get(SHIFT.GlobalContext).Id;

                if (PosWindow.ActiveWindowInstance is CloseCheckWindow)
                {
                    t.OwningCheckNumber = CCBindings.SelectedSeatIndex;
                }
                else
                {
                    t.OwningCheckNumber = 0;
                }
                t.UserId = SHIFT.Current.User.Id;
                SHIFT.SelectedOrder.TRANSACTIONS.Add(t);
                SHIFT.GlobalContext.SaveChanges();

                if (PosWindow.ActiveWindowInstance is CloseCheckWindow)
                {
                    CCBindings.RefreshThreadSafe(true);
                    CloseCheckWindow.StartChangeDueBlink();
                }
                if (KIOSK.HasLocalPrinter())
                {
                    KIOSK k = KIOSK.Get(SHIFT.GlobalContext);
                    if (k.PrintCreditReceiptOnPay)
                    {
                        CREDIT_TRANSACTION_PRINT_PACKET ctpp = new CREDIT_TRANSACTION_PRINT_PACKET();
                        ctpp.Transaction = t;
                        ctpp.TransactionResponse = respMessage;
                        Int64 localPrinterId = (Int64)KIOSK.Get(SHIFT.GlobalContext).LocalPrinterId;
                        string localPrinterPath = SHIFT.GlobalContext.PRINTERS
                            .Where(p => p.Id == localPrinterId).FirstOrDefault().Path;

                        int seatIndex = 0;
                        if (PosWindow.ActiveWindowInstance is CloseCheckWindow)
                        {
                            seatIndex = CCBindings.SelectedSeatIndex;
                        }
                        for (int i = 0; i < k.NumberCreditReceipts; i++)
                        {
                            PrintSpool.PrintCreditCardReceipt(localPrinterPath, SHIFT.SelectedOrder, ctpp, seatIndex);
                        }
                    }

                    if (k.PrintItemizedReceiptOnPay)
                    {
                        int seatIndex = 0;
                        if (PosWindow.ActiveWindowInstance is CloseCheckWindow)
                        {
                            seatIndex = CCBindings.SelectedSeatIndex;
                        }
                        ORDER order = SHIFT.SelectedOrder;
                        PrintSpool.PrintItemizedReceipt(
                            (long)k.LocalPrinterId,
                            order,
                            order.SelCheckAutoGratutiy,
                            order.SelCheckRoundedSubtotal,
                            order.SelCheckTax, seatIndex);

                    }
                }

                if (partialAmountAuthed)
                {
                    UMD.ShowGuiMessageSTASafe_NOTHROW("Please note that only part of the purchase price was authorized because there were not enough funds on the card to cover the entire balance due.");
                }
                else
                {
                    if (PosWindow.ActiveWindowInstance is QuickOrderWindow)
                    {
                        decimal amountDue = SHIFT.SelectedOrder.CheckUnpaidGrandTotal(0);
                        if (amountDue <= ORDER.ROUND_ERROR)
                        {
                            CloseTable.Close();
                        }
                    }
                }
            }
            else
            {
                UMD.ShowGuiMessageSTASafe_NOTHROW("The processor returned an error code of ["
                    + respMessage.CmdStatus + "] and a message of [" + respMessage.TextResponse + "].");
            }
            SHIFT.GlobalContext.SaveChanges();
        }

        static byte[] GetBytes(string str)
        {
            byte[] bytes = new byte[str.Length * sizeof(char)];
            System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        } 

        public static void ProcessCreditCardSwiped(MagneticCard magCard)
        {
            try
            {
                if (Constants.IsTrainingMode)
                {
                    UMD.ShowGuiMessageSTASafe_NOTHROW("To protect cardholder data and remain PCI Compliant, credit card and gift card processing are not enabled while in training mode.");
                }
                else
                {

                    CloseCheckWindow.StopChangeDueBlink();
                    string response = "";
                    if (CCBindings.PrepaidMessageBox != null)
                    {
                        CCBindings.PrepaidMessageBox.Close();
                        CCBindings.PrepaidMessageBox = null;

                        string cardType = magCard.CardType;

                        if (cardType != null && cardType.Equals(MagneticCard.CONST_MERCURY_GIFT_STRING))
                        {
                            ///First request the balance.
                            Payments.Prepaid.Balance bdata = new Payments.Prepaid.Balance();
                            bdata.InvoiceNo = SHIFT.SelectedOrder.Id.ToString();
                            bdata.Purchase = CashMoneyBox.TotalAmount.ToString("#0.00");
                            bdata.IpPort = "9100";
                            App.InvokeStartStoryBoard_NOTHROW();
                            System.Threading.Tasks.Task.Factory.StartNew(() => ProcessPrepaidBalance(magCard, bdata));
                        }
                        else
                        {
                            UMD.ShowGuiMessageSTASafe_NOTHROW("The card that you swiped does not appear to be a gift card.");
                        }
                    }
                    else
                    {
                        bool canClose = false;

                        if (PosWindow.ActiveWindowInstance is QuickOrderWindow)
                        {
                            canClose = SHIFT.SelectedOrder.CanCloseSpecificCheck(0);
                        }
                        else
                        {
                            canClose = SHIFT.SelectedOrder.CanCloseCheck;
                        }
                        if (!canClose)
                        {
                            bool foundDuplicate = false;
                            decimal unpaid = 0;

                            if (PosWindow.ActiveWindowInstance is QuickOrderWindow)
                            {
                                unpaid = SHIFT.SelectedOrder.CheckUnpaidGrandTotal(0);
                            }
                            else
                            {
                                if (SHIFT.SelectedOrder.EqualPayIsActive == true)
                                {
                                    unpaid = SHIFT.SelectedOrder.EqualPayAmountDue();
                                }
                                else
                                {
                                    unpaid = SHIFT.SelectedOrder.SelCheckUnpaidGrandTotal;
                                }
                            }
                            App.CashMoneyWindowInstance.ShowMe(unpaid, CashMoneyBox.CashBoxType.TYPE_PAYMENT);
                            if (CashMoneyBox.LastResponse == UserResponse.ENTER && CashMoneyBox.TotalAmount != 0)
                            {
                                if (CashMoneyBox.TotalAmount <= unpaid)
                                {
                                    CACHE c = new CACHE();
                                    ///I HAVE REMOVED THE ENCRPTION ALGORITHM TO PROTECT MY EXISTING CUSTOMERS FROM HACKS FROM MAKING THIS PUBLIC
                                    ///KNOWLEDGE.  YOU MUST USE A STRONG ENCRYPTION ALGORITHM HERE AND NEVER STORE THE FULL MAG STRIPE EVER, NEVER.
                                    //string pan = Crpt.ToString(cache.BinP);
                                    //string expire = Crpt.ToString(cache.BinE);
                                    string pan = "1111111111111111";
                                    string expire = "1111";
                                    //c.BinP = Crpt.ToBin(magCard.Pan);
                                    //c.BinE = Crpt.ToBin(magCard.Expire);
                                    c.BinP = GetBytes(pan);
                                    c.BinE = GetBytes(expire);
                                    c.DateTime = DateTime.Now;

                                    ///See if this card has been used this business day.
                                    IEnumerable<CACHE> caches = SHIFT.GlobalContext.CACHES.Where(csh => csh.BinP.Equals(c.BinP));
                                    if (caches.Any())
                                    {
                                        ///It has been used.  Now we need to see if it is attached to the current order and if so,
                                        ///is it the exact same transaction amount?
                                        foreach (CACHE cache in caches)
                                        {
                                            if (SHIFT.SelectedOrder.TRANSACTIONS
                                                .Where(t => (t.ExtId == cache.Id) &&
                                                    (t.Base == CashMoneyBox.TotalAmount && (t.TransactionType == TRANSACTION.CONST_PAY_GIFT
                                                    || t.TransactionType == TRANSACTION.CONST_PAY_CREDIT))).Any())
                                            {
                                                foundDuplicate = true;
                                                break;
                                            }
                                        }
                                    }

                                    ///Only continue if a duplicate transaction for this order and same amount do not
                                    ///exist.
                                    if (!foundDuplicate)
                                    {
                                        bool isPrepaidGift = false;
                                        bool partialAmountAuthed = false;
                                        string cardType = magCard.CardType;
                                        if (cardType != null)
                                        {
                                            if (cardType.Equals(MagneticCard.CONST_MERCURY_GIFT_STRING))
                                            {
                                                isPrepaidGift = true;

                                                ///First request the balance.
                                                Payments.Prepaid.Balance bdata = new Payments.Prepaid.Balance();
                                                bdata.InvoiceNo = SHIFT.SelectedOrder.Id.ToString();
                                                bdata.Purchase = CashMoneyBox.TotalAmount.ToString("#0.00");
                                                bdata.IpPort = "9100";
                                                response = bdata.Request(magCard, SHIFT.GlobalContext, SHIFT.Current.UserId);

                                                TRANSACTION_RESPONSE balResponse = new TRANSACTION_RESPONSE();
                                                balResponse.Parse(response);

                                                if (balResponse.CmdStatusOk)
                                                {
                                                    decimal balance = Convert.ToDecimal(balResponse.Balance);
                                                    Payments.Prepaid.Sale data = new Payments.Prepaid.Sale();
                                                    data.InvoiceNo = SHIFT.SelectedOrder.Id.ToString();
                                                    data.IpPort = "9100";

                                                    if (balance >= CashMoneyBox.TotalAmount)
                                                    {
                                                        data.Purchase = CashMoneyBox.TotalAmount.ToString("#0.00");
                                                        App.InvokeStartStoryBoard_NOTHROW();
                                                        System.Threading.Tasks.Task.Factory.StartNew(() =>
                                                        {
                                                            response = data.Request(magCard);
                                                            Constants.MainDispatcher.Invoke(
                                                                new Action(() => FinishCardRequest(c, response, isPrepaidGift, magCard, partialAmountAuthed)));
                                                        });
                                                    }
                                                    else if (balance > 0)
                                                    {
                                                        data.Purchase = balance.ToString("#0.00");

                                                        App.InvokeStartStoryBoard_NOTHROW();
                                                        System.Threading.Tasks.Task.Factory.StartNew(() =>
                                                        {
                                                            response = data.Request(magCard);
                                                            partialAmountAuthed = true;
                                                            Constants.MainDispatcher.Invoke(
                                                                new Action(() => FinishCardRequest(c, response, isPrepaidGift, magCard, partialAmountAuthed)));
                                                        });
                                                    }
                                                    else
                                                    {
                                                        UMD.ShowGuiMessageSTASafe_NOTHROW("The card has a zero balance.");
                                                        return;
                                                    }
                                                }
                                                else
                                                {
                                                    UMD.ShowGuiMessageSTASafe_NOTHROW("The processor returned an error code of ["
                                                        + balResponse.CmdStatus + "] and a message of [" + balResponse.TextResponse + "].");
                                                    return;
                                                }
                                            }
                                            else
                                            {
                                                PreAuth data = new PreAuth();
                                                data.InvoiceNo = SHIFT.SelectedOrder.Id.ToString();
                                                data.Purchase = CashMoneyBox.TotalAmount.ToString("#0.00");
                                                data.Authorize = data.Purchase;
                                                ///Now send the request.
                                                App.InvokeStartStoryBoard_NOTHROW();
                                                System.Threading.Tasks.Task.Factory.StartNew(() =>
                                                {
                                                    response = data.Request(magCard, SHIFT.GlobalContext, SHIFT.Current.UserId);
                                                    Constants.MainDispatcher.Invoke(new Action(() => FinishCardRequest(c, response, isPrepaidGift, magCard, partialAmountAuthed)));
                                                });
                                            }
                                        }
                                    }
                                    else
                                    {
                                        UMD.ShowGuiMessageSTASafe_NOTHROW("To protect your customers from duplicated transactions, you cannot authorize a transaction using the same credit card combined with the same dollar amount on the same order.");
                                    }
                                }
                                else
                                {
                                    UMD.ShowGuiMessageSTASafe_NOTHROW("You cannot authorize a transaction for more than the amount owed by the customer.");
                                }
                            }
                        }
                        else
                        {
                            UMD.ShowGuiMessageSTASafe_NOTHROW("You cannot accept a payment for a check that has no balance.");
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Constants.ErrorDispatcher.Notify(Error.EType.E_PLACEHOLDER, e);
            }
        }
        public static void PaymentCardSwiped(string magSwipe)
        {

            try
            {
                if ((!ModalWindow.ModalDialogIsOpen || ModalWindow.CurrentlyOpenInstance is AskForCCMessageBox) && Constants.HasPaymentGateway)
                {
                    MagneticCard magCard = new MagneticCard(magSwipe);
                    if (magCard.IsValid)
                    {
                        ///If the current window is the close check window, then the user has swiped a credit card
                        ///and they are attempting to add a form of payment via credit.  Go ahead and process the
                        ///swipe.
                        if (PosWindow.ActiveWindowInstance is CloseCheckWindow)
                        {
                            ProcessCreditCardSwiped(magCard);
                        }
                        else if (PosWindow.ActiveWindowInstance is OrderWindow)
                        {
                            if (Constants.IsTrainingMode)
                            {
                                UMD.ShowGuiMessageSTASafe_NOTHROW("To protect cardholder data and remain PCI Compliant, credit card and gift card processing are not enabled while in training mode.");
                            }
                            else
                            {

                                if (SALE.CurrentPrepaidRequest != null)
                                {
                                    ///This is a prepaid transaction.  We need to authorize or place funds on the card.
                                    App.InvokeStartStoryBoard_NOTHROW();
                                    System.Threading.Tasks.Task.Factory.StartNew(() => SALE.ProcessPrepaidRequest_NOTHROW(magCard));
                                }
                                else if (!App.CashMoneyWindowInstance.IsActive) ///If the cash box window is open, then we are waiting on a card swipe
                                ///for gift cards.
                                {
                                    if (SYSCONFIG.Get(SHIFT.GlobalContext).AllowCardOnFile)
                                    {
                                        CACHE c = new CACHE();
                                        ///I HAVE REMOVED THE ENCRPTION ALGORITHM TO PROTECT MY EXISTING CUSTOMERS FROM HACKS FROM MAKING THIS PUBLIC
                                        ///KNOWLEDGE.  YOU MUST USE A STRONG ENCRYPTION ALGORITHM HERE AND NEVER STORE THE FULL MAG STRIPE EVER, NEVER.
                                        //string pan = Crpt.ToString(cache.BinP);
                                        //string expire = Crpt.ToString(cache.BinE);
                                        string pan = "1111111111111111";
                                        string expire = "1111";
                                        //c.BinP = Crpt.ToBin(magCard.Pan);
                                        //c.BinE = Crpt.ToBin(magCard.Expire);
                                        c.BinP = GetBytes(pan);
                                        c.BinE = GetBytes(expire);
                                        c.DateTime = DateTime.Now;

                                        SHIFT.GlobalContext.CACHES.AddObject(c);
                                        SHIFT.GlobalContext.SaveChanges();

                                        if (SHIFT.SelectedOrder.CardOnFileExtId != null)
                                        {
                                            CACHE oldCache =
                                                SHIFT.GlobalContext.CACHES.
                                                Where(cache => cache.Id == SHIFT.SelectedOrder.CardOnFileExtId).FirstOrDefault();

                                            if (oldCache != null)
                                            {
                                                SHIFT.GlobalContext.CACHES.DeleteObject(oldCache);
                                            }
                                        }
                                        SHIFT.SelectedOrder.CardOnFileExtId = c.Id;
                                        SHIFT.SelectedOrder.HasCardOnFile = true;
                                        SHIFT.GlobalContext.SaveChanges();
                                    }
                                    POBindings.SetHeadOfTable(magCard.Name);
                                }
                            }
                        }
                        else if (PosWindow.ActiveWindowInstance is QuickOrderWindow && !POBindings.IsPendingAction())
                        {
                            if (SALE.CurrentPrepaidRequest != null)
                            {
                                ///This is a prepaid transaction.  We need to authorize or place funds on the card.
                                App.InvokeStartStoryBoard_NOTHROW();
                                System.Threading.Tasks.Task.Factory.StartNew(() => SALE.ProcessPrepaidRequest_NOTHROW(magCard));
                            }
                            else
                            ///If the cash box window is open, then we are waiting on a card swipe
                            ///for gift cards.
                            {
                                ///Send the order and take them to the payment window.
                                POBindings.SubmitOrderClicked();
                                ProcessCreditCardSwiped(magCard);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ///Get rid of the magnetic data, it is no longer needed.
                Constants.ErrorDispatcher.Notify(Error.EType.E_GEN_PAYMENT_CARD_SWIPED, ex);
            }
        }

        public static void CreditCardImage_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            try
            {
                if (Constants.HasPaymentGateway && !SHIFT.SelectedOrder.CanCloseCheck)
                {
                    if (Constants.IsTrainingMode)
                    {
                        UMD.ShowGuiMessageSTASafe_NOTHROW("To protect cardholder data and remain PCI Compliant, credit card and gift card processing are not enabled while in training mode.");
                    }
                    else
                    {
                        CreditCardEntryWindow ccew = new CreditCardEntryWindow();
                        ccew.ShowDialog();
                        if (CreditCardEntryWindow.HasCompleteInformation)
                        {
                            if (Constants.IsQuickServiceMode && PosWindow.ActiveWindowInstance is QuickOrderWindow)
                            {
                                POBindings.SubmitOrderClicked();
                            }
                            MagneticCard mc = new MagneticCard(CreditCardEntryWindow.Pan, CreditCardEntryWindow.Expire);
                            MagCardDelegates.ProcessCreditCardSwiped(mc);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Constants.ErrorDispatcher.Notify(Error.EType.E_PLACEHOLDER, ex);
            }
        }
        /// <summary>
        /// This delegate will be called when the payment engine detects that the oceanside login plastic card
        /// expected token has been detected in a mag card swipe.
        /// </summary>
        /// <param name="posLoginToken"></param>
        public static void PosLoginCardSwiped(String posLoginToken)
        {
            try
            {
                if (!ModalWindow.ModalDialogIsOpen && !String.IsNullOrEmpty(posLoginToken))
                {
                    ScreenSaverDelegates.LastMouseTick = Environment.TickCount;

                    if (PosWindow.ActiveWindowInstance is LoginWindow || PosWindow.ActiveWindowInstance is ScreenSaver)
                    {
                        ScreenSaverDelegates.CheckScreenSaverNeedsExit();
                        LIBindings.Login(posLoginToken, true);
                    }
                    else if (PosWindow.ActiveWindowInstance is TimeSheetWindow)
                    {
                        TSBindings.OceansideCardSwiped(posLoginToken);
                    }
                    else
                    {
                        USER user = USER.GetManagerForSwipe(posLoginToken);
                        if (null != user)
                        {
                            PosWindow.ManagerCardWasSwiped = true;
                            PosWindow.ManagerIdThatSwiped = user.Id;

                            if (PosWindow.ActiveWindowInstance is FinancialWindow)
                            {
                                FinancialBindings.Refresh();
                            }
                            else if (PosWindow.ActiveWindowInstance is CloseCheckWindow)
                            {
                                CCBindings.RefreshThreadSafe();
                            }
                            else if (PosWindow.ActiveWindowInstance is ControlPanelWindow)
                            {
                                CPBindings.Refresh();
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Constants.ErrorDispatcher.Notify(Error.EType.E_GEN_POS_CARD_LOGIN_SWIPED, ex);
            }
        }
    }
}
